package sg4c.frontend;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.io.IOException;
import java.util.List;
import sg4c.backend.BackendConnector;
import sg4c.frontend.command.GraphicsCommand;
import sg4c.protocol.DataInputProtocol;
import sg4c.protocol.DataOutputProtocol;
import sg4c.protocol.GraphicDescriptionProtocol;
import sg4c.protocol.KeyEventProtocol;
import sg4c.protocol.MouseEventProtocol;
import sg4c.protocol.TextInputProtocol;
import sg4c.protocol.TextOutputProtocol;
import sg4c.util.RecordTime;

public class GraphicsFrontend extends DoubleBufferedApplet {

    public static final int CMD_SETUP_GRAPHICS = 0xee01;
    public static final int CMD_ENDING = 0x0fe;
    public static final int CMD_SETUP_SCREEN = 0x01;
    public static final int CMD_SETUP_FPS = 0x02;
    public static final int CMD_SETUP_SMOOTH = 0x03;
    public static final int CMD_FONT_CREATE = 0x04;
    protected BackendConnector conn;
    protected final GraphicDescriptionProtocol sgproto;
    protected DataInputProtocol backendInput;
    protected DataOutputProtocol backendOutput;
    protected GraphicsContext context = null;
    protected boolean enabledSmoothing = false;

    protected GraphicsFrontend() {
        sgproto = new GraphicDescriptionProtocol();
    }

    public GraphicsFrontend(String exepath) throws IOException {
        this();
        this.conn = new BackendConnector(exepath);
        backendInput = new TextInputProtocol(conn.in);
        backendOutput = new TextOutputProtocol(conn.out);
    }

    @Override
    public void init() {
        super.init();
        initDisplay();

        // Add listener
        RedirectingMouseListener mouseListener = new MouseEventProtocol(backendOutput);
        addMouseListener(mouseListener);
        addMouseMotionListener(mouseListener);

        RedirectingKeyListener keyListener = new KeyEventProtocol(backendOutput);
        addKeyListener(keyListener);
    }

    @Override
    public void start() {
        super.start();
    }

    public List<GraphicsCommand> recvCommands() {
        try {
            sgproto.updateFrame(backendOutput);
            return sgproto.parseAll(backendInput);
        } catch (IOException ex) {
            ex.printStackTrace();
            System.exit(1);
            return null;
        }
    }

    public void draw(Graphics g) {
        beginDraw();
//        Dimension screenSize = getSize();
        Graphics2D graphics = (Graphics2D) g;

        if (context == null) {
            context = new GraphicsContext(graphics);


            //enable anti aliasing
            if (enabledSmoothing) {
                graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            }
            setupGraphics();
        }

        prepareGraphicCommands();

        endDraw();
    }

    protected void prepareGraphicCommands() {
        // Parsing commands did not take as much time as first expected.
        // Must of the time spent on execution of commands.
        //RecordTime t1 = new RecordTime();
        final List<GraphicsCommand> listOfCmds=recvCommands();

        //t1.show("list");
        //RecordTime t2 = new RecordTime();
        for (GraphicsCommand cmd : listOfCmds) {
            cmd.execute(context);
        }
        //t2.show("exec");
        // Code to put current framerate on screen.
        context.getGraphics().setFont(new Font("Dialog", 0, 10));
        context.getGraphics().setPaint(new Color(0,0,0));
        context.getGraphics().drawString(String.format("%4.1f", apparentFramerate()), 30, 30);
    }

    private void setupGraphics() {
        try {
            backendOutput.writeInt(CMD_SETUP_GRAPHICS);
            backendOutput.flush();
            int cmd;
            backendInput.next();
            while ((cmd = backendInput.readInt()) != (CMD_ENDING)) {
                if (cmd == (CMD_FONT_CREATE)) {
                    String fontname = backendInput.readString();
                    int id = backendInput.readInt();
                    int pt = backendInput.readInt();

                    context.createFont(id, fontname, pt);

                    int fontHeight = context.getFontMetrics(id).getHeight();

                    backendOutput.writeInt(fontHeight);
                    backendOutput.flush();


                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
            System.exit(1);
        }
    }

    private void initDisplay() {
        try {
            int cmd;
            backendInput.next();
            while ((cmd = backendInput.readInt()) != (CMD_ENDING)) {
                if (cmd == (CMD_SETUP_SCREEN)) {
                    int w = backendInput.readInt();
                    int h = backendInput.readInt();
                    setSize(w, h);
                } else if (cmd == (CMD_SETUP_FPS)) {
                    int requestedFps = backendInput.readInt();

                    if (requestedFps == 0) {//backend driven
                        requestedFps = 60;
                    }
                    setFramerate(requestedFps);
                } else if (cmd == (CMD_SETUP_SMOOTH)) {
                    enabledSmoothing = true;
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
            System.exit(1);
        }
    }
}

